﻿using System;
using System.Collections.Generic;
using System.Text;

namespace AutoUpdateService.Server
{
    using System.Threading.Tasks;
    using DotNetty.Buffers;
    using DotNetty.Handlers.Logging;
    using DotNetty.Transport.Bootstrapping;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Sockets;
    using DotNetty.Codecs;
    using DotNetty.Handlers.Timeout;
    public class Server
    {
        private static Server m_Instance { set; get; } = null;

        public static Server getInstance()
        {
            if (m_Instance == null)
                m_Instance = new Server();

            return m_Instance;
        }

        private IEventLoopGroup bossGroup { set; get; } = null;

        private IEventLoopGroup workerGroup { set; get; } = null;

        private ServerBootstrap bootstrap { set; get; } = null;

        private IChannel serverChannel { set; get; } = null;

        private int serverPort { set; get; } = 3143;

        public EventHandler.ClientConnectEventHandler connectEvent { set; get; } = null;

        public EventHandler.ClientDisconnectEventHandler disconnectEvent { set; get; } = null;

        public EventHandler.ClientReceiveDataEventHandler receiveDataEvent { set; get; } = null;

        public async void runServerAsync(int port)
        {
            if (port > 0)
            {
                this.serverPort = port;
            }

            this.bossGroup = new MultithreadEventLoopGroup(1);

            this.workerGroup = new MultithreadEventLoopGroup();

            try
            {
                this.bootstrap = new ServerBootstrap();

                this.bootstrap.Group(this.bossGroup, this.workerGroup);


                this.bootstrap
                    .Channel<TcpServerSocketChannel>()
                    .Option(ChannelOption.SoBacklog, 100)
                    .Handler(new LoggingHandler("SRV-LSTN"))
                    .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                    {
                        IChannelPipeline pipeline = channel.Pipeline;
                        pipeline.AddLast("idleStateHandler", new IdleStateHandler(30, 0, 0));
                        pipeline.AddLast("idleStateTrigger", new ServerIdleStateTrigger());
                        pipeline.AddLast(new LoggingHandler("SRV-CONN"));
                        pipeline.AddLast("framing-enc", new LengthFieldPrepender(4));
                        pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(int.MaxValue , 0, 4, 0, 4));
                        pipeline.AddLast("AutoUpdate", new ServerHandler(this));
                    }));


                this.serverChannel = await this.bootstrap.BindAsync(this.serverPort);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public async void stopServerAsync()
        {
            await this.serverChannel.CloseAsync();

            Console.WriteLine("close");

            await Task.WhenAll(this.bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                 this.workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));



            if (this.serverChannel.CloseCompletion.IsCompleted)
            {
                Console.WriteLine("closed:" + this.serverChannel.CloseCompletion.IsCompleted.ToString());
                this.serverChannel = null;
            }
        }

        public bool status()
        {


            if (this.serverChannel != null)
                return this.serverChannel.Active;
            else
                return false;
        }

        public void sendMessage(IChannel channel, string message)
        {
            try
            {
                this.sendMessage(channel, Encoding.UTF8.GetBytes(message));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void sendMessage(IChannel channel, byte[] message)
        {
            try
            {
                Console.WriteLine("send from server");
                IByteBuffer buffer = Unpooled.Buffer(ushort.MaxValue);
                buffer.WriteBytes(message);
                channel.WriteAndFlushAsync(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        public void sendMessage(IChannelHandlerContext context, byte[] message)
        {
            try
            {
                Console.WriteLine("send from server");
                IByteBuffer buffer = Unpooled.Buffer(ushort.MaxValue);
                buffer.WriteBytes(message);
                context.WriteAndFlushAsync(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }
    }
}
